home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / program / ctlib100.zip / INSTALL.LZH / TABLES.TXT < prev    next >
Text File  |  1996-10-12  |  11KB  |  204 lines

  1.                        CHAPTER 10
  2.                          Tables 
  3.            
  4. This chapter discusses in detail the table objects included in the Containers Library.  It describes how to use the tables and the different types of tables available. 
  5.  
  6. In this chapter you will learn:
  7.  
  8.   - What are tables, fields, and field structures?
  9.   - How to use tables
  10.   - The characteristics of each type of table
  11.  
  12.  
  13. What are tables, fields and field structures?
  14.  
  15. Tables are stream-based sequences in which each data item has one or more fields.  There are two types of tables provided in the Containers Library: TTable, which stores records, and TObjectTable, which stores objects.  Each data item is analogous to a Pascal record, with the fields in the record equivalent to the fields in the table.  Entire records or individual fields of a data item may be accessed.  The Cursors Library provides a table specific cursor which takes advantage of the field access in tables.  The field structure for the table, or listing of the order, types, and size of fields in a table, is specified on creation of the table and is static for the life of the table.  In order to change the field structure you must create a new table with the desired field structure and copy the data to it.  A field may be of any of the data types declared in the ctFields unit's FieldTypes constant, except ftObjectID, which is used internally. 
  16.  
  17. Tables provide the "flat-file" database structure necessary for implementing most any database format.  By using the TTable and TObjectTable containers, you can easily incorporate simple database tables into your applications, with support for up to 2 billion (MaxLongInt) records and 18 field types.  You can also implement indexed and multi-indexed tables in your applications with very little effort, by combining the powerful indexing capabilities of B trees with a table.
  18.  
  19.  
  20. Using the Tables
  21.  
  22. There are several steps in creating and using a new table: 
  23.  
  24. 1- Define the field structure
  25. 2- Define the stream
  26. 3- Register the field and field structure types for
  27.    stream usage
  28. 4- Initialize the table
  29. 5- Add/delete/modify data
  30. 6- Dispose the table,
  31. 7- And last, dispose the stream.   
  32.  
  33. The first step in using a table is defining the table's field structure.  Defining the field structure is equivalent to defining a record or object type in Pascal.  The field structure should mirror the record or object structure which the table will hold.  The field order, size, and data type specified in the table must match the record or object's field structure exactly, or unpredictable results will occur.  Field structures use the TField and TFieldStructure objects in the ctFields unit.  Field information is stored in TField's, which are stored in a TFieldStructure.  Descendant objects of both types may be used.
  34.  
  35.  
  36. Creating a New Table 
  37.  
  38. The following shows the Pascal declaration of a record structure for an address (see TABLE1.PAS).  The order of field insertion into the field structure determines the order in which the fields should occur in the corresponding record. 
  39.  
  40.   type
  41.     TAddressRec = record
  42.       Line1: string[50];
  43.       Line2: string[50];
  44.       City: string[50];
  45.       State: string[50];
  46.     end;
  47.  
  48. The corresponding field structure declaration would be as follows: 
  49.  
  50.   uses
  51.     ctFields;
  52.  
  53.   function AddressFieldStructure: PFieldStructure;
  54.   var
  55.     FieldStructure: PFieldStructure;
  56.     Field: PField;
  57.     Name: TFieldName;
  58.     i: Integer;
  59.   begin
  60.     FieldStructure := New(PFieldStructure, Init(4,1));
  61.     if (FieldStructure <> nil) then
  62.       for i := 1 to 4 do
  63.       begin
  64.         case i of
  65.           1: Name := 'Line1';
  66.           2: Name := 'Line2';
  67.           3: Name := 'City';
  68.           4: Name := 'State';
  69.         end;
  70.         Field := New(PField, Init(Name, ftString, 50, 0));
  71.         if (Field <> nil)
  72.           then FieldStructure^.Insert(Field)
  73.           else Halt;
  74.       end;
  75.     AddressFieldStructure := FieldStructure;
  76.   end;
  77.  
  78. The ctFields unit declares many NewXXXXField functions for simplifying the field declaration process. 
  79.  
  80. Ownership of the field structure passed to TTable.Init is conditional.  If the initialization is successful, the field structure is used within the table and destructed by the table.  If the table's initialization fails, the field structure is not destructed.  This is so that the calling program can let the end user attempt to correct the problem without having to redefine the field structure.  If initialization fails, don't forget to dispose of the field structure if it is no longer needed. 
  81.  
  82. Once the field structure has been defined, you must decide where to store the data.  Tables may be stored on any read/write stream, regardless of its location (disk, memory, remote, etc.).  Tables themselves provide no buffering of data items.  Buffering is left to the stream.  Using a stream with a buffer size which is several times the size of a data item (recommended) provides excellent performance.  The stream passed to TTable.Init is not owned by the stream and must always be disposed when use of the stream is finished.   This allows tables to be placed anywhere on a stream.  The first byte of the table is stored at the current position in the stream when the stream is passed to a table's Init constructor. 
  83.  
  84.  
  85. Initializing a table 
  86.  
  87. Once the field structure and stream type have been declared, you are now ready to initialize the table. 
  88.  
  89.   const
  90.     TempFile = 'TEMP.DAT';
  91.     BufferSize = 2048;
  92.  
  93.   var
  94.     Stream: PStream;
  95.     FieldStructure: PFieldStructure;
  96.     Table: PTable;
  97.  
  98.   begin
  99.     { Don't forget to register the     }
  100.     { field and field structure types! }
  101.     RegisterType(RFieldStructure);
  102.     RegisterType(RField);
  103.   
  104.     FieldStructure := AddressFieldStructure;
  105.     Stream := New(PBufStream, Init(TempFile, stCreate,
  106.       BufferSize));
  107.     Table := New(PTable,Init(FieldStructure, Stream)); 
  108.     if Table = nil then
  109.     begin
  110.       Dispose(FieldStructure, Done);
  111.       Dispose(Stream, Done);
  112.       WriteLn('Error initializing table.');
  113.       Halt; 
  114.     end;
  115.  
  116.     { add, delete, and modify data }
  117.     Dispose(Table,Done);    
  118.     Dispose(Stream,Done);
  119.   end;
  120.  
  121.  
  122. Opening an Existing Table
  123.            
  124. To open a table which was previously created, simply declare the stream using stOpen as the file mode.  Be sure not to use the stCreate constant, as this will erase all data held in the stream.  Then just pass the newly instantiated stream to the Open constructor (rather than Init) and that's it!  For example:
  125.  
  126.   var
  127.     Stream: PBufStream;
  128.     Table: PTable;
  129.  
  130.   begin
  131.     Stream := New(PBufStream, Init(TempFile, stOpen,
  132.       BufferSize));
  133.     if Stream = nil then
  134.       Halt;
  135.     Table := New(PTable, Open(Stream));
  136.     if Table = nil then
  137.     begin
  138.       Dispose(Stream, Done);
  139.       Halt;
  140.     end;
  141.     Dispose(Table, Done);
  142.   end;
  143.  
  144.  
  145. Data Management in Tables
  146.  
  147. Inserting new data items into a table is as inserting items into any other container:  it is done through the Insert method.  By default, all inserted data items are placed at the end of the table, causing the stream to grow.  Retrieving data items is done as in any other sequence, by using the At method.  Finally, to update an existing item, simply call AtPut with the updated record information.   
  148.  
  149. In addition to the standard data retrieval and updating methods of a sequence, TTable offers several methods that will let you retrieve and update individual fields (AtField and AtPutField), find the name or length of a field (FieldName and FieldLength), the number of decimals in a field (FieldDecimals), the number of fields in a row (FieldCount), etc.  The beginning of a field in the data item is determined by a combination of the field type and size parameters in the table's field structure.   
  150.  
  151. If a record contains pointers to data, such as a PString, and you intend to use the field access methods of a table, you must override TTable.FieldOffset to give the correct information.  Also, AtField and AtPutField will need to overridden for correct field processing and FreeItem, GetItem and PutItem to ensure proper data handling. 
  152.  
  153. As with any container, all data items retrieved from a table should be disposed using DoneItem.  Since tables are stream based data storage structures, new pointers are allocated by At.  Failing to call DoneItem when you are finished with the pointer will result in a memory leak.   
  154.  
  155.  
  156. Object tables
  157.  
  158. TTable only can store variables which are a simple type or a record structure.  TObjectTable provides a mechanism for storing any object which is a descendant of TObject.  You must remember to register each object type which will be stored in the table for stream registration using RegisterType.  Failing to do so causes a run-time error. There are a few minor differences between object and non-object tables.
  159.  
  160. The first difference is in the field structure declaration.  The data area in a stream for an object must be large enough to hold the object with each field at its maximum length.  In other words, if the object has a field which is a PString, the field structure must reserve enough space to store the string in its maximum length.  This results in potentially minor wasted stream resources, but is better than always writing to the end of the stream to prevent overwriting data following the object. 
  161.  
  162. Using an object-oriented version of the previous example (see TABLE2.PAS), the object type and field structure declaration would be declared as follows: 
  163.  
  164.   type
  165.     Str50: string[50];
  166.  
  167.   PMyObject = ^TMyObject;
  168.   TMyObject = object(TObject)
  169.       Line1: PString;
  170.       Line2: PString;
  171.       City: PString;
  172.       State: PString;
  173.     constructor Init (ALine1, ALine2, ACity,
  174.       AState: Str50);
  175.     constructor Load (var S: TStream);
  176.     destructor Done; virtual;
  177.     procedure Store (var S: TStream); virtual;
  178.   end;
  179.  
  180.   var
  181.     Structure: PFieldStructure;
  182.     Field: PField;
  183.  
  184.   begin
  185.     Structure := New(PFieldStructure, Init(4, 1));
  186.     if (Structure <> nil) then
  187.       for i := 1 to 4 do
  188.       begin
  189.         case i of
  190.           1: Name := 'Line1';
  191.           2: Name := 'Line2';
  192.           3: Name := 'City';
  193.           4: Name := 'State';
  194.         end;
  195.         Field := NewPStringField(Name, 50);
  196.         if (Field <> nil)
  197.           then FieldStructure^.Insert(Field)
  198.           else Halt;
  199.       end;
  200.   end;
  201.  
  202. All tables which store objects and will use field access to data must override AtField, AtPutField, and FieldOffset because of the internal variation in field position within objects.  The fields may be pointers themselves, but the field offset is also affected by the levels of inheritance for the object.
  203.  
  204. Other than these minor differences, the use of a TObjectTable is identical to a TTable.